Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | /** * API route for skill tutorial management * * POST /api/curriculum/[playerId]/tutorial - Handle tutorial actions * - action: 'complete' - Mark a tutorial as completed * - action: 'skip' - Record that the tutorial was skipped * - action: 'override' - Teacher override (requires reason) * * GET /api/curriculum/[playerId]/tutorial?skillId=xxx - Get tutorial status */ import { NextResponse } from 'next/server' import { withAuth } from '@/lib/auth/withAuth' import { canPerformAction } from '@/lib/classroom' import { getSkillTutorialProgress, markTutorialComplete, recordTutorialSkip, applyTutorialOverride, enableSkillForPractice, } from '@/lib/curriculum/progress-manager' import { getSkillTutorialConfig } from '@/lib/curriculum/skill-unlock' import { getUserId } from '@/lib/viewer' /** * GET - Get tutorial progress for a specific skill */ export const GET = withAuth(async (request, { params }) => { try { const { playerId } = (await params) as { playerId: string } if (!playerId) { return NextResponse.json({ error: 'Player ID required' }, { status: 400 }) } // Authorization check const userId = await getUserId() const canView = await canPerformAction(userId, playerId, 'view') if (!canView) { return NextResponse.json({ error: 'Not authorized' }, { status: 403 }) } const { searchParams } = new URL(request.url) const skillId = searchParams.get('skillId') if (!skillId) { return NextResponse.json({ error: 'Skill ID required' }, { status: 400 }) } const progress = await getSkillTutorialProgress(playerId, skillId) const config = getSkillTutorialConfig(skillId) return NextResponse.json({ progress, tutorialAvailable: !!config, config: config ? { title: config.title, description: config.description, problemCount: config.exampleProblems.length, } : null, }) } catch (error) { console.error('Error fetching tutorial progress:', error) return NextResponse.json({ error: 'Failed to fetch tutorial progress' }, { status: 500 }) } }) /** * POST - Handle tutorial actions */ export const POST = withAuth(async (request, { params }) => { try { const { playerId } = (await params) as { playerId: string } if (!playerId) { return NextResponse.json({ error: 'Player ID required' }, { status: 400 }) } const body = await request.json() const { skillId, action, reason } = body if (!skillId) { return NextResponse.json({ error: 'Skill ID required' }, { status: 400 }) } if (!action || !['complete', 'skip', 'override'].includes(action)) { return NextResponse.json( { error: 'Valid action required (complete, skip, or override)' }, { status: 400 } ) } let progress switch (action) { case 'complete': // Mark tutorial complete and enable skill for practice progress = await markTutorialComplete(playerId, skillId) // Automatically enable the skill for practice after completing tutorial await enableSkillForPractice(playerId, skillId) break case 'skip': // Record that the tutorial was skipped progress = await recordTutorialSkip(playerId, skillId) break case 'override': // Teacher override - requires reason if (!reason) { return NextResponse.json( { error: 'Reason required for teacher override' }, { status: 400 } ) } progress = await applyTutorialOverride(playerId, skillId, reason) // Also enable the skill for practice await enableSkillForPractice(playerId, skillId) break } return NextResponse.json({ success: true, progress, }) } catch (error) { console.error('Error handling tutorial action:', error) return NextResponse.json({ error: 'Failed to handle tutorial action' }, { status: 500 }) } }) |